#pragma once // 防止头文件被重复包含

#include <iostream>
#include <string>
#include <vector>
#include <semaphore.h> //信号量头文件，属于线程库pthread
#include <pthread.h>   //原生线程库

template <typename T>
class RingQueue
{
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    // 上锁
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    // 解锁
    void Unlock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }

public:
    RingQueue(int cap)
        : _ring_queue(cap), _cap(cap), _productor_step(0), _consumer_step(0) // 构造指定环形队列大小,并且p,c下标初始0，为空队列
    {
        // sem_init参数说明，第三个为信号量设置的初始值，第二个0多线程可见，非0不仅多线程可见，多进程也可见
        // 所有设置0就ok，当前进程里多线程可见就行了
        sem_init(&_room_sem, 0, _cap); // 空间资源刚开始是全都未用的，空队列
        sem_init(&_data_sem, 0, 0);    // 空队列，数据资源没有，数据信号量这把计数器为0
        pthread_mutex_init(&_productor_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }
    // 由于321原则，空和满的情况，信号量完美解决了不用判断和通知的代码，申请成功是一定有资源的，生产者和消费者同步与互斥了
    // 但是，生产者和生产者之间，消费者和消费者之间，也需要互斥，所以需要两把锁，一把关生产者之间互斥，一把关消费者之间互斥
    // 为什么？不和阻塞队列一样一把锁呢？因为阻塞队列是一份资源，环形队列多块小资源，我这块小资源生产和你那块消费，80%高并发
    // 所以，生产者和消费者之间不共用锁，所以需要两把锁，管好各自这边人
    //  Enqueue---生产者
    void Enqueue(const T &in)
    {
        // 生成行为
        P(_room_sem); // 申请空间信号量
        // 申请成功，是一定有资源的
        // 只有有资源的人，才能进来竞争锁，连资源都没有，进来竞争锁，还有再等资源排队，浪费时间,所以有资源的人才能竞争锁，执行以下代码
        Lock(_productor_mutex);
        _ring_queue[_productor_step++] = in; // 生产
        _productor_step %= _cap;             // 处理循环越界问题
        Unlock(_productor_mutex);
        V(_data_sem); // V操作计数器++
    }
    // Pop -----消费者
    void Pop(T *out) // 输出型参数带出弹出数据
    {
        // 消费行为
        P(_data_sem); // 申请消费资源
        // 申请成功是一定有资源的
        Lock(_consumer_mutex);
        *out = _ring_queue[_consumer_step++];
        _consumer_step %= _cap;
        Unlock(_consumer_mutex);
        V(_room_sem);
    }
    ~RingQueue()
    {
        // 析构释放信号量资源
        sem_destroy(&_room_sem);
        sem_destroy(&_data_sem);
        pthread_mutex_destroy(&_productor_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
    }

private:
    // 1. 环形队列
    std::vector<T> _ring_queue; // 数组模拟环形队列
    int _cap;                   // 环形队列的容量上限
    // 2. 生产者和消费者的下标
    int _productor_step; // 生产者的脚步
    int _consumer_step;  // 消费者的脚步
    // 3. 信号量（实现同步与互斥）
    sem_t _room_sem; // 管理空间资源的一把计数器， 生产者关心
    sem_t _data_sem; // 管理数据资源的一把计数器， 消费者关心
    // 4.定义锁，维护多生产多消费之间的互斥关系
    pthread_mutex_t _productor_mutex; // 管生产者的锁
    pthread_mutex_t _consumer_mutex;  // 管消费者的锁
};